using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using CloudShot.Editor.CanvasFigures;
using CloudShot.Editor.Tools.States;
using CloudShot.Interfaces;
using CloudShot.Logic;
using CloudShot.Properties;

namespace CloudShot.Editor.Tools
{
  /// <summary>
  ///   Pointer tool
  /// </summary>
  internal class PointerTool : Tool
  {
    private readonly ICursorService _cursorService;
    private readonly Section _toolSection;
    private readonly Section _pointerSection;

    private IPointerToolState _state;

    public override Cursor Cursor
    {
      get { return GetHoverCursor(Canvas); }
    }

    private IPointerToolState State
    {
      get { return _state; }
      set
      {
        if (_state != null && _state != value)
          _state.Release();

        _state = value;
        ShowHint(_state == null);

        if (_state != null)
          _state.Enter();
      }
    }

    public PointerTool(IDrawingCanvas drawingCanvas, ICursorService cursorService, Section toolSection, Section pointerSection) : base(drawingCanvas)
    {
      _cursorService = cursorService;
      _toolSection = toolSection;
      _pointerSection = pointerSection;
    }

    /// <summary>
    ///   Left mouse button is pressed
    /// </summary>
    /// <param name="e"></param>
    public override void OnMouseDown(MouseEventArgs e)
    {
      State = null;
      Point point = e.Location;
      _toolSection.Enabled = false;

      // Test for resizing (only if control is selected, cursor is on the handle)
      foreach (CanvasObject selectionedObject in Canvas.GraphicsList.SelectedObjects)
      {
        int handleNumber = selectionedObject.HitTest(point);

        if (handleNumber > 0)
        {
          // keep resized object in class members
          State = new ResizeObjectState(Canvas, selectionedObject, handleNumber);
          break;
        }
      }

      // Crop selection
      if (State == null && !Canvas.Crop.IsSelectionState)
      {
        int cropHandle = Canvas.Crop.HitTest(point);

        if (cropHandle >= 0)
        {
          State = new CropSelectionState(Canvas, _cursorService, cropHandle, point);
        }
      }

      // Test for move (cursor is on the object)
      if (State == null)
      {
        CanvasObject canvasObject = Canvas.GraphicsList.GetObjectAtPoint(point, false);

        if (canvasObject != null)
        {
          State = new MoveObjectState(Canvas, _cursorService, canvasObject, point);
        }
      }

      // Net selection
      if (State == null)
      {
        State = new NetSelectionState(Canvas, point);
      }

      Canvas.CaptureMouse = true;
      Canvas.RefreshCanvas();
    }

    public override void OnKeyDown(KeyEventArgs e)
    {
      base.OnKeyDown(e);

      if ((e.KeyCode & Keys.Space) != 0)
      {
        Canvas.Crop.IsDragOnlyByEdges = false;

        if (State == null)
          _cursorService.Cursor = GetHoverCursor(Canvas);
      }

      if (State != null)
        State.OnKeyDown(e);
    }

    public override void OnKeyUp(KeyEventArgs e)
    {
      base.OnKeyUp(e);

      if ((e.KeyCode & Keys.Space) != 0)
      {
        Canvas.Crop.IsDragOnlyByEdges = true;

        if (State == null)
          _cursorService.Cursor = GetHoverCursor(Canvas);
      }

      if (State != null)
        State.OnKeyUp(e);
    }

    /// <summary>
    ///   Mouse is moved.
    ///   None button is pressed, ot left button is pressed.
    /// </summary>
    /// <param name="e"></param>
    public override void OnMouseMove(MouseEventArgs e)
    {
      var point = new Point(e.X, e.Y);

      // set cursor when mouse button is not pressed
      if (e.Button == MouseButtons.None)
      {
        var cursor = GetHoverCursor(Canvas);
        _cursorService.Cursor = cursor;
        return;
      }

      if (e.Button != MouseButtons.Left)
        return;

      if (State != null)
        State.OnMouseMove(point);
    }

    /// <summary>
    ///   Right mouse button is released
    /// </summary>
    /// <param name="e"></param>
    public override void OnMouseUp(MouseEventArgs e)
    {
      //NOTE: when it fails 
      //start drawing rect. press esc holding mouse button
      //then release mouse button and TAAA-DAAA! 
      if (State == null)
        return;

      _toolSection.Enabled = true;

      State = null;

      Canvas.CaptureMouse = false;
      Canvas.RefreshCanvas();
    }

    public override void Enter()
    {
      ShowHint(true);
      _pointerSection.Enabled = true;
    }

    public override void Leave()
    {
      _pointerSection.Enabled = false;
      State = null;
      ShowHint(false);
    }

    private void ShowHint(bool show)
    {
      if (show && Canvas.GraphicsList.SelectedObjects.Any())
      {
        bool showTextHint = Canvas.GraphicsList.SelectedObjects.All(o=>o is CanvasText);
        Canvas.ShowHint(showTextHint? HintResources.PointerTool_HasTextSelection: HintResources.PointerTool_HasSelection);
      }
      else
      {
        Canvas.HideHint();
      }
    }

    #region Cursor Logic

    private static Cursor GetHoverCursor(IDrawingCanvas canvas)
    {
      Point point = canvas.GetLastMousePosition();

      var canvasObjectsAndCrop = new List<CanvasObject>();
      canvasObjectsAndCrop.AddRange(canvas.GraphicsList);
      canvasObjectsAndCrop.Add(canvas.Crop);

      return GetObjectCursor(canvasObjectsAndCrop, point);
    }

    private static Cursor GetObjectCursor(List<CanvasObject> canvasObjects, Point point)
    {
      //first check only the trackers
      Cursor cursor = FindCursor(canvasObjects, point, handle => handle > 0);

      if (cursor == null)
      {
        //then check object internals
        cursor = FindCursor(canvasObjects, point, handle => handle == 0);
      }

      return cursor ?? AppCursors.Default;
    }

    private static Cursor FindCursor(IEnumerable<CanvasObject> canvasObjects, Point point, Predicate<int> handlePredicate)
    {
      return (from obj in canvasObjects 
              let handle = obj.HitTest(point) 
              where handlePredicate(handle) 
              select obj.GetHandleCursor(handle)).FirstOrDefault();
    }

    #endregion
  }
}